[ElementTiming] Replace responseEnd with loadTime This CL replaces responseEnd with loadTime in ElementTiming. Before, responseEnd would be used and would be problematic for memory cached images and for inline images (data scheme). We change it to loadTime, the time at which LayoutObject::ImageNotifyFinished is called. This is the same time as when LargestContentfulPaint computes loadTime. As they are both under runtime flags and the hooks are not unified, for simplicity we compute the timestamp again. This will not be too expensive for ElementTiming, as the timestamp is gated on the existence of the elementtiming attribute. To store the loadTime, we change images_notified_ to be a HashMap which now stores the timestamp corresponding to loadTime and whether the image has painted (after being loaded). Bug: 982046, 879270 Change-Id: I69da42c9250cc36c567df5da285ef1a9a357b554 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1710840 Commit-Queue: Nicolás Peña Moreno <npm@chromium.org> Reviewed-by: Steve Kobes <skobes@chromium.org> Cr-Commit-Position: refs/heads/master@{#680117} diff --git a/element-timing/images-repeated-resource.html b/element-timing/images-repeated-resource.html index 9bc8b5f..a6ad7ac 100644 --- a/element-timing/images-repeated-resource.html +++ b/element-timing/images-repeated-resource.html
@@ -13,8 +13,10 @@ <script> let beforeRender; let numEntries = 0; - let responseEnd1; - let responseEnd2; + let loadTime1; + let loadTime2; + let renderTime1; + let renderTime2; let img; let img2; const index = window.location.href.lastIndexOf('/'); @@ -26,23 +28,33 @@ } const observer = new PerformanceObserver( t.step_func(function(entryList) { - entryList.getEntries().forEach(entry => { - // Easier to check the |element| attribute here since element ID is the same for both images. - checkElement(entry, pathname, entry.identifier, 'image_id', beforeRender, null); - checkNaturalSize(entry, 100, 100); - if (entry.identifier === 'my_image') { - ++numEntries; - responseEnd1 = entry.responseEnd; - assert_equals(entry.element, img); - } - else if (entry.identifier === 'my_image2') { - ++numEntries; - responseEnd2 = entry.responseEnd; - assert_equals(entry.element, img2); - } - }); + assert_equals(entryList.getEntries().length, 1); + const entry = entryList.getEntries()[0]; + // Easier to check the |element| attribute here since element ID is the same for both images. + checkElement(entry, pathname, entry.identifier, 'image_id', beforeRender, null); + checkNaturalSize(entry, 100, 100); + if (entry.identifier === 'my_image') { + ++numEntries; + loadTime1 = entry.loadTime; + renderTime1 = entry.renderTime; + assert_equals(entry.element, img); + + img2 = document.createElement('img'); + img2.src = 'resources/square100.png'; + img2.setAttribute('elementtiming', 'my_image2'); + img2.setAttribute('id', 'image_id'); + document.body.appendChild(img2); + beforeRender = performance.now(); + } + else if (entry.identifier === 'my_image2') { + ++numEntries; + loadTime2 = entry.loadTime; + renderTime2 = entry.renderTime; + assert_equals(entry.element, img2); + } if (numEntries == 2) { - assert_equals(responseEnd1, responseEnd2); + assert_greater_than(loadTime2, loadTime1, 'Second image loads after first.'); + assert_greater_than(renderTime2, renderTime1, 'Second image renders after first'); t.done(); } }) @@ -57,16 +69,9 @@ img.setAttribute('elementtiming', 'my_image'); img.setAttribute('id', 'image_id'); document.body.appendChild(img); - - img2 = document.createElement('img'); - img2.src = 'resources/square100.png'; - img2.setAttribute('elementtiming', 'my_image2'); - img2.setAttribute('id', 'image_id'); - document.body.appendChild(img2); - beforeRender = performance.now(); }; - }, 'Element with elementtiming attribute is observable.'); + }, 'Elements with elementtiming and same src are observable.'); </script> </body>